package com.kiss.fw.utils.objectpool;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.kiss.fw.utils.Constants;
import com.kiss.fw.utils.Util;

public abstract class AbstractObjectPool<E> {
	private static Logger logger = Logger.getLogger(AbstractObjectPool.class);
	private Map<E, Long> locked;
	private List<E> unlocked;
	private Class<E> poolClass;
	private int maxSize;
	private int minSize;
	
	protected abstract void configure(E e) throws Exception;
	protected abstract void clean(E _object);
	
	protected AbstractObjectPool(Class<E> _class) {
		// setting the pool class
		poolClass = _class;
		
		// generating the max size property file key
		StringBuffer sb = new StringBuffer(Constants.Property.OBJECT_POOL_MAX_SIZE);
		sb.append(poolClass.getName());
		
		// upating the max size
		maxSize = Util.parseInt(Util.getProperty(sb.toString()), 50);
		
		// generating the min size property file key
		sb.delete(0,  sb.length());
		sb.append(Constants.Property.OBJECT_POOL_MIN_SIZE);
		sb.append(poolClass.getName());
		
		// updating the min size
		minSize = Util.parseInt(Util.getProperty(sb.toString()), 10);
		
		// initializing the objects place-holders
		unlocked = new ArrayList<E>(maxSize);
		locked = new HashMap<E, Long>(maxSize);
		
		// populating the object pool with the minimum required objects
		for(int i=0; i < minSize; i++) {
			try {
				unlocked.add(create());
			}
			catch (Exception _exception) {
				logger.error("Error initializing object pool.");
				throw Constants.Exceptions.OBJECT_POOL_INITIALIZE_EXCEPTION;
			}
		}
	}
	
	public E create() throws Exception{
		E instance = poolClass.newInstance();
		configure(instance);
		return instance;
	}
	
	public synchronized E getObject() {
		E returnVal = null;
		
		if(unlocked.size() > 0) {
			// if there are objects in the list, get the first object
			returnVal = unlocked.get(0);
		}
		else {
			// TODO: Get from property
			int maxSize = 10;
			
			if(unlocked.size() + locked.size() > maxSize) {
				// TODO: Throw exception
			}
			else {
				try {
					returnVal = create();
				}
				catch(Exception _exception) {
					logger.error("Error creating object", _exception);
				}
			}
		}
		
		// TODO: Implement timeout
		locked.put(returnVal, null);
		
		return returnVal;
	}
	
	public void returnObject(E _object) {
		if(_object != null) {
			clean(_object);
			
			synchronized(locked) {
				if(locked.containsKey(_object)) {
					locked.remove(_object);
					unlocked.add(_object);
				}
				else {
					logger.warn("Invalid object returned: " +_object);
				}
			}
		}
	}
}